<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>线程池</title>
</head>
<body>
  <script>
    class TaskWorker extends Worker {
      constructor(notifyAvaliable, ...workerArgs) {
        super(...workerArgs);

        // 初始化为不可用状态
        this.avaliable = false;
        this.resolve = null;
        this.reject = null;

        // 线程池会传递回调
        // 以便工作者线程发出它需要新任务的信号
        this.notifyAvaliable = notifyAvaliable;

        // 线程脚本在完全初始化之后
        // 会发送一条 "ready" 消息
        this.onmessage = this.setAvaliable;
      }

      // 由线程池调用，以分派新任务
      dispatch({ resolve, reject, postMessageArgs }) {
        this.avaliable = false;

        this.onmessage = ({ data }) => {
          resolve(data);
          this.setAvaliable();
        };

        this.onerror = e => {
          reject(e);
          this.setAvaliable();
        };

        this.postMessage(...postMessageArgs);
      }

      setAvaliable() {
        this.avaliable = true;
        this.resolve = null;
        this.reject = null;
        this.notifyAvaliable();
      }
    }

    class WorkerPool {
      constructor(poolSize, ...workerArgs) {
        this.taskQueue = [];
        this.workers = [];
        // 初始化线程池
        for (let i = 0; i < poolSize; i++) {
          this.workers.push(new TaskWorker(this.dispatchIfAvailable, ...workerArgs));
        }
      }

      // 把任务推入队列
      enqueue(...postMessageArgs) {
        return new Promise((resolve, reject) => {
          this.taskQueue.push({ resolve, reject, postMessageArgs });
          this.dispatchIfAvailable();
        });
      }

      // 把任务发送给下一个空闲的线程（如果有的话）
      dispatchIfAvailable() {
        if (!this.taskQueue.length) return;
        for (const worker of this.workers) {
          if (worker.available) {
            const a = this.taskQueue.shift();
            worker.dispatch(a);
            break;
          }
        }
      }

      // 终止所有工作者线程
      close() {
        for (const worker of this.workers) {
          worker.terminate();
        }
      }
    }
  </script>

  <script>
    const totalFloats = 1E8;
    const numTasks = 20;
    const floatsPerTask = totalFloats / numTasks;
    const numWorkers = 4;
    // 创建线程池
    const pool = new WorkerPool(numWorkers, './27_2_11_worker.js');

    // 填充浮点值数组
    const arrayBuffer = new SharedArrayBuffer(4 * totalFloats);
    const view = new Float32Array(arrayBuffer);
    const partialSumPromises = [];
    for (let i = 0; i < totalFloats; ++i) {
      view[i] = Math.random();
    }
    for (let i = 0; i < totalFloats; i += floatsPerTask) {
      partialSumPromises.push(
        pool.enqueue({
          startIdx: i,
          endIdx: i + floatsPerTask,
          arrayBuffer
        })
      );
    }
    // 等待所有期约完成，然后求和
    Promise.all(partialSumPromises)
      .then(partialSums => partialSums.reduce((x, y) => x + y))
      .then(console.log);
    //（在这个例子中，和应该约等于 1E8/2）
    // 49997075.47203197
  </script>
</body>
</html>